/*******************************************************************************
 * This file is part of Crunch Graphics.
 *
 * Crunch Graphics is free software: you can redistribute it and/or modify it under the
 * terms of the GNU Lesser General Public License as published by the Free Software
 * Foundation, either version 3 of the License, or (at your option) any later version.
 *
 * Crunch Graphics is distributed in the hope that it will be useful, but WITHOUT ANY
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
 * PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License along with
 * Crunch Graphics.  If not, see <http://www.gnu.org/licenses/>.
 ******************************************************************************/

package com.crunch.graphics.gpu;

import javax.media.opengl.GL;
import javax.media.opengl.GL3;
import java.util.Arrays;

/** Encapsulates the state of the which buffers should be drawn to on the GPU.
 */
public class GpuDrawBufferState extends GpuObject {
	/** Sets the state of the GPU to the state represented by this object.
	 */
	public void bind() {
		getGpuState().bind(this);
	}

	/** Sets which buffers are drawn to.
	 */
	public boolean setDrawBuffers(int[] buffers) {
		if (buffers.length > getGpuState().getMaxDrawBuffers()) {
			return false;
		}
		drawBufferCount = buffers.length;
		System.arraycopy(buffers, 0, drawBuffers, 0, buffers.length);
		return true;
	}

	/** Returns the number of buffers being drawn to.
	 */
	public int getDrawBufferCount() {
		return drawBufferCount;
	}

	/** Returns the array of buffers being drawn to.
	 */
	public int[] getDrawBuffers() {
		return Arrays.copyOfRange(drawBuffers, 0, drawBufferCount);
	}

	GpuDrawBufferState(GpuState state) {
		super(state);
		drawBufferCount = 0;
	}

	/** Copy constructor.
	 */
	public GpuDrawBufferState(GpuDrawBufferState other) {
		super(other.getGpuState());
		drawBufferCount = other.drawBufferCount;
		System.arraycopy(other.drawBuffers, 0, drawBuffers, 0, MAX_DRAW_BUFFERS);
	}

	// package level, because GpuState accesses this constant
	static final int MAX_DRAW_BUFFERS = 16; // also impose GPU limit

	private int drawBufferCount;
	private int[] drawBuffers = new int[MAX_DRAW_BUFFERS];

	// compares changes against current state and apply differences
	void apply(GpuDrawBufferState currentState) {
		if (drawBufferCount == currentState.drawBufferCount) {
			boolean allEqual = true;
			for (int i = 0; i < drawBufferCount; ++i) {
				if (drawBuffers[i] != currentState.drawBuffers[i]) {
					allEqual = false;
					break;
				}
			}
			if (allEqual) {
				return;
			}
		}

		getGpuState().getGl().glDrawBuffers(drawBufferCount, drawBuffers, 0);
		currentState.drawBufferCount = drawBufferCount;
		System.arraycopy(drawBuffers, 0, currentState.drawBuffers, 0, drawBufferCount);
	}

	// sets values to the current OpenGL state
	void getCurrentState() {
		int[] buf = new int[1];
		for (int i = 0; i < getGpuState().getMaxDrawBuffers(); ++i) {
			getGpuState().getGl().glGetIntegerv(GL3.GL_DRAW_BUFFER0 + i, buf, 0);
			drawBuffers[i] = buf[0];
		}
		// compute the number of draw buffers by starting at the end and finding the first one that isn't GL_NONE
		drawBufferCount = getGpuState().getMaxDrawBuffers();
		for (int i = 0; i < getGpuState().getMaxDrawBuffers(); ++i) {
			int t = getGpuState().getMaxDrawBuffers() - i - 1;
			if (drawBuffers[t] == GL.GL_NONE) {
				--drawBufferCount;
			} else {
				break;
			}
		}
	}
}